/*
 * chat_hash.c
 *
 *  Created on: 2012-6-30
 *      Author: rendong
 */

#include <stdlib.h>
#include <string.h>

#include "chat_hash.h"

//#define DEBUG_HASH

static const size_t primes[] = {
 53,   97,   193,  389,
 769,  1543,  3079,  6151,
 12289,  24593,  49157,  98317,
 196613,  393241,  786433,  1572869,
 3145739, 6291469, 12582917, 25165843,
 50331653, 100663319, 201326611, 402653189,
 805306457, 1610612741
};

static const int prime_table_length = sizeof(primes)/sizeof(primes[0]);

/**
 * 初始化hash table
 */
void chat_hash_init(chat_hash_table_t * hashtable, size_t capacity)
{
	if (hashtable == NULL)
		return;

	int i;
	size_t nlimit = capacity;

	for(i=0; i<prime_table_length; i++){
		if (primes[i] > nlimit){
			nlimit = primes[i];
			break;
		}
	}

	hashtable->nlimit = nlimit;
	hashtable->size = 0;
	size_t memsize = sizeof(chat_hash_table_item_t *) * nlimit;
	hashtable->items = (chat_hash_table_item_t **)malloc(memsize);
	memset(hashtable->items, 0, memsize);
}


/**
 * 向hash表中加入数据
 * 键存在，会覆盖老的值
 * @return 键存在，返回1， 否则返回0
 */
int chat_hash_put(chat_hash_table_t * hashtable, char *key, void *value)
{
	chat_hash_table_item_t * item = (chat_hash_table_item_t*)malloc(sizeof(chat_hash_table_item_t));

	// 内部复制一份key，防止被外部删除，导致程序错误!!!hash del时 free
	item->key = strdup(key);

	item->value = value;
	item->hash = chat_inline_hash_func(item->key, strlen(item->key)) % hashtable->nlimit;
	item->next = NULL;

#ifdef DEBUG_HASH
	chat_log_debug("chat_hash_put:%x, size:%d key:%s hash:%d\n", hashtable, hashtable->size, item->key, item->hash);
#endif

	chat_hash_table_item_t * sitem = hashtable->items[item->hash];
	if (sitem != NULL){

		if (strcmp(sitem->key, item->key) == 0){// 相同的key
			hashtable->items[item->hash] = item;
			free(sitem);
#ifdef DEBUG_HASH
	chat_log_debug("chat_hash_put:same key\n");
#endif
			return 1;
		}

		// 有冲突
		while(sitem->next != NULL){
			 sitem = sitem->next;

			if (strcmp(sitem->key, item->key) == 0){// 相同的key
				hashtable->items[item->hash] = item;
				free(sitem);
#ifdef DEBUG_HASH
	chat_log_debug("chat_hash_put:cong tu key\n");
#endif
				return 1;
			}
		}

		sitem->next = item;
		hashtable->size++;
	}else{
		hashtable->items[item->hash] = item;
		hashtable->size++;
#ifdef DEBUG_HASH
	chat_log_debug("chat_hash_put:normal put key size:%d\n", hashtable->size);
#endif
	}

	return 0;
}


/**
 * 获取数据
 */
void * chat_hash_get(chat_hash_table_t * hashtable, char *key)
{
	size_t hash = chat_inline_hash_func(key, strlen(key)) % hashtable->nlimit;
#ifdef DEBUG_HASH
	chat_log_debug("chat_hash_get %x key:%s hash:%d size:%d\n", hashtable, key, hash, hashtable->size);
#endif
	chat_hash_table_item_t * sitem = hashtable->items[hash];

	if (sitem == NULL){
#ifdef DEBUG_HASH
	chat_log_debug("chat_hash_get null\n");
#endif
		return NULL;
	}else{
		if(strcmp(sitem->key, key) == 0){
#ifdef DEBUG_HASH
	chat_log_debug("chat_hash_get normal\n");
#endif
			return sitem->value;
		}else{
			while(sitem->next != NULL){
				sitem = sitem->next;

				if(strcmp(sitem->key, key) == 0){
#ifdef DEBUG_HASH
	chat_log_debug("chat_hash_get next normal\n");
#endif
					return sitem->value;
				}
			}
		}
	}
#ifdef DEBUG_HASH
	chat_log_debug("chat_hash_get null get\n");
#endif
	return NULL;
}


/**
 * 删除
 */
void chat_hash_del(chat_hash_table_t * hashtable, char *key)
{
	size_t hash = chat_inline_hash_func(key, strlen(key)) % hashtable->nlimit;

#ifdef DEBUG_HASH
	chat_log_debug("chat_hash_del:%x key:%s hash:%d\n", hashtable, key, hash);
#endif

	chat_hash_table_item_t * sitem = hashtable->items[hash];
	chat_hash_table_item_t * tmp;

	if (sitem == NULL)
		return;
	else{
		if(strcmp(sitem->key, key) == 0){
			hashtable->items[hash] = sitem->next;
			free(sitem->key);
			free(sitem);
			hashtable->size--;
		}else{
			while(sitem->next != NULL){
				if(strcmp(sitem->next->key, key) == 0){
					tmp = sitem->next;
					sitem->next = tmp->next;
					free(tmp->key);
					free(tmp);
					hashtable->size--;
					return;
				}

				sitem = sitem->next;
			}
		}
	}
}
